package com.hzw.saas.service.storage.service;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.StrUtil;
import com.hzw.saas.api.storage.ICatalogService;
import com.hzw.saas.api.storage.IResourceFileService;
import com.hzw.saas.api.storage.IThumbnailService;
import com.hzw.saas.api.storage.InxChunkService;
import com.hzw.saas.api.storage.bo.InxChunkBO;
import com.hzw.saas.api.storage.bo.ResourceFileStorageBO;
import com.hzw.saas.api.storage.enums.FileIntegrityEnum;
import com.hzw.saas.api.storage.enums.ResourceStatusEnum;
import com.hzw.saas.api.storage.query.ResourceFileUpdateQuery;
import com.hzw.saas.common.config.exception.DefaultSaasException;
import com.hzw.saas.common.config.query.LogicQuery;
import com.hzw.saas.common.storage.pojo.FileStorage;
import com.hzw.saas.common.storage.pojo.InxChunkStorage;
import com.hzw.saas.common.storage.service.IChunkStorageService;
import com.hzw.saas.common.storage.util.ChecksumUtil;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.common.util.enums.AssertEnum;
import com.hzw.saas.common.util.enums.ConflictModeEnum;
import com.hzw.saas.service.storage.util.StorageUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.io.File;
import java.util.Objects;

/**
 * @author zzl
 * @since 04/24/2021
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class InxChunkServiceImpl implements InxChunkService {

    private final PlatformTransactionManager transactionManager;

    private final ICatalogService catalogService;
    private final IResourceFileService fileService;
    private final IChunkStorageService chunkStorageService;
    private final IThumbnailService thumbnailService;

    @Override
    public boolean exist(String chunkCatalog, InxChunkBO chunkInfo) {
        String chunkSupPath = catalogService.getCacheSupPath(chunkCatalog, chunkInfo.getFileId());
        String chunkName = String.valueOf(chunkInfo.getChunkNumber());

        // 校验digest
        if (StrUtil.isNotBlank(chunkInfo.getDigest())) {
            String chunkDigest = chunkStorageService.getDigest(chunkSupPath, chunkName, chunkInfo.getDigestType());
            return Objects.equals(chunkDigest, chunkInfo.getDigest());
        }
        if (Objects.nonNull(chunkInfo.getCurrentChunkSize())) {
            long chunkLength = chunkStorageService.getLength(chunkSupPath, chunkName);
            return Objects.equals(chunkLength, chunkInfo.getCurrentChunkSize());
        }
        return chunkStorageService.exist(chunkSupPath, chunkName);
    }

    @Override
    public InxChunkBO saveChunk(String chunkCatalog, InxChunkBO chunkInfo, byte[] data) {
        InxChunkStorage chunkStorage = MapperUtil.nf().map(chunkInfo, InxChunkStorage.class);
        chunkStorage.setSuperPath(catalogService.getCacheSupPath(chunkCatalog, chunkInfo.getFileId()));
        chunkStorageService.saveChunk(chunkStorage, data);
        return chunkInfo;
    }

    @Override
    public void mergeChunks(String fileId, String chunkCatalog, Integer storageMode) {
        ResourceFileStorageBO fileInfo = fileService.getFileWithStorage(fileId,
            LogicQuery.eq(ResourceStatusEnum.NORMAL.getCode()), LogicQuery.none(), AssertEnum.FOUND);
        if (Objects.equals(FileIntegrityEnum.NORMAL.getCode(), fileInfo.getIntegrity())) {
            log.debug("合并文件分片结束，文件已被合并过.");
            return;
        }
        if (Objects.equals(FileIntegrityEnum.MERGING.getCode(), fileInfo.getIntegrity())) {
            throw DefaultSaasException.build("文件正在合并中").status(HttpStatus.BAD_REQUEST);
        }
        // backup
        Integer originFileIntegrity = fileInfo.getIntegrity();
        // 标识当前合并中状态
        fileService.updateFileIntegrity(fileInfo.getPid(), FileIntegrityEnum.MERGING.getCode());

        // 开启事务
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            // 合并文件
            String fileSupPath = catalogService.getFileSupPath(fileInfo.getCatalog(), fileInfo.getResourceKey());
            FileStorage fileStorage = StorageUtil.convert(fileInfo, fileSupPath);
            String chunkSupPath = catalogService.getCacheSupPath(chunkCatalog, fileInfo.getPid());
            File fileData = chunkStorageService.mergeChunks(fileStorage, chunkSupPath, true, ConflictModeEnum.COVER_TAR);
            // 标识当前合并状态，更新校验和
            ResourceFileUpdateQuery updateQuery = new ResourceFileUpdateQuery();
            updateQuery.setPid(fileId);
            updateQuery.setIntegrity(FileIntegrityEnum.NORMAL.getCode());
            /*if (Objects.isNull(fileInfo.getWeakChecksum())) {
                long weakChecksum = ChecksumUtil.adler32(fileData);
                updateQuery.setWeakChecksum(weakChecksum);
            }*/
            // 强校验和MD5
            if (StrUtil.isBlank(fileInfo.getStrongChecksum())) {
                String strongChecksum = ChecksumUtil.md5Hex(fileData);
                updateQuery.setStrongChecksum(strongChecksum);
            }
            // 长度
            if (Objects.isNull(fileInfo.getLength())) {
                updateQuery.setLength(fileData.length());
            }
            fileService.updateFile(updateQuery);
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            fileService.updateFileIntegrity(fileInfo.getPid(), originFileIntegrity);
            throw e;
        }


        ThreadUtil.execute(()->{
            // 创建缩略图等
            thumbnailService.saveThumbnail(fileId);
            fileService.dealStorageMode(fileId, storageMode, false);
        });

    }

    @Override
    public void deleteChunk(String fileId, String chunkCatalog, long chunkNumber) {
        String chunkSupPath = catalogService.getCacheSupPath(chunkCatalog, fileId);
        chunkStorageService.deleteChunk(chunkSupPath, String.valueOf(chunkNumber));
    }

    @Override
    public void deleteChunks(String fileId, String chunkCatalog) {
        String chunkSupPath = catalogService.getCacheSupPath(chunkCatalog, fileId);
        chunkStorageService.deleteChunks(chunkSupPath);
    }
}
